﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO.Packaging;
using System.IO;
using System.Xml;

namespace TestPlanWordWriter.Common.Word
{
    public class FlatOpcConverter
    {
        private static readonly XNamespace packageNs = "http://schemas.microsoft.com/office/2006/xmlPackage";
        private static readonly XNamespace relationshipsNs = "http://schemas.openxmlformats.org/package/2006/relationships";

        public static void FlatToOpc(XDocument doc, string docxPath, Stream documentStream)
        {
            using (Package package = Package.Open(documentStream, FileMode.Create))
            {
                AddContentParts(doc, package);
                AddRelationships(doc, package);

                documentStream.Flush();
            }
        }

        private static void AddRelationships(XDocument doc, Package package)
        {
            foreach (XElement xmlPart in GetRelationshipParts(doc))
            {
                string name = (string)xmlPart.Attribute(packageNs + "name");
                // add the package level relationships
                if (name == "/_rels/.rels")
                {
                    foreach (XElement xmlRel in xmlPart.Descendants(relationshipsNs + "Relationship"))
                    {
                        string id = (string)xmlRel.Attribute("Id");
                        string type = (string)xmlRel.Attribute("Type");
                        string target = (string)xmlRel.Attribute("Target");
                        string targetMode = (string)xmlRel.Attribute("TargetMode");
                        if (targetMode == "External")
                        {
                            package.CreateRelationship(new Uri(target, UriKind.Absolute), TargetMode.External, type, id);
                        }
                        else
                        {
                            package.CreateRelationship(new Uri(target, UriKind.Relative), TargetMode.Internal, type, id);
                        }
                    }
                }
                else
                // add part level relationships
                {
                    string directory = name.Substring(0, name.IndexOf("/_rels"));
                    string relationshipsFilename = name.Substring(name.LastIndexOf('/'));
                    string filename = relationshipsFilename.Substring(0, relationshipsFilename.IndexOf(".rels"));
                    PackagePart fromPart = package.GetPart(new Uri(directory + filename, UriKind.Relative));
                    foreach (XElement xmlRel in xmlPart.Descendants(relationshipsNs + "Relationship"))
                    {
                        string id = (string)xmlRel.Attribute("Id");
                        string type = (string)xmlRel.Attribute("Type");
                        string target = (string)xmlRel.Attribute("Target");
                        string targetMode = (string)xmlRel.Attribute("TargetMode");
                        if (targetMode == "External")
                        {
                            fromPart.CreateRelationship(new Uri(target, UriKind.Absolute), TargetMode.External, type, id);
                        }
                        else
                        {
                            fromPart.CreateRelationship(new Uri(target, UriKind.Relative), TargetMode.Internal, type, id);
                        }
                    }
                }
            }
        }

        private static void AddContentParts(XDocument doc, Package package)
        {
            foreach (XElement xmlPart in GetContentElements(doc))
            {
                string name = (string)xmlPart.Attribute(packageNs + "name");
                string contentType = (string)xmlPart.Attribute(packageNs + "contentType");
                if (contentType.EndsWith("xml"))
                {
                    Uri partUri = new Uri(name, UriKind.Relative);
                    PackagePart part = package.CreatePart(partUri, contentType, CompressionOption.SuperFast);
                    using (Stream partStream = part.GetStream(FileMode.Create))
                    {
                        using (XmlWriter xmlWriter = XmlWriter.Create(partStream))
                        {
                            xmlPart.Element(packageNs + "xmlData").Elements().First().WriteTo(xmlWriter);
                        }
                    }
                }
            }
        }

        private static IEnumerable<XElement> GetRelationshipParts(XDocument document)
        {
            return document.Root.Elements().Where(p => IsRelationshipElement(p));
        }

        private static bool IsRelationshipElement(XElement partElement)
        {
            string contentType = (string)partElement.Attribute(packageNs + "contentType");
            return (contentType == "application/vnd.openxmlformats-package.relationships+xml");
        }

        private static IEnumerable<XElement> GetContentElements(XDocument document)
        {
            return document.Root.Elements().Where(p => IsContentElement(p));
        }

        private static bool IsContentElement(XElement partElement)
        {
            string contentType = (string)partElement.Attribute(packageNs + "contentType");
            return (contentType != "application/vnd.openxmlformats-package.relationships+xml");
        }
    }
}
